﻿using com.superscene.ui;
using com.superscene.util;
using parkMonitor.bll;
using parkMonitor.entity;
using parkMonitor.language;
using parkMonitor.model;
using parkMonitor.server.monitorServer;
using parkMonitor.server.uiLogServer;
using parkMonitor.viewModel.detail;
using parkMonitor.viewModel.devInfoPanel;
using parkMonitor.viewModel.objectTree;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace parkMonitor.server {

	public class MonitorModel {
		public Action<ObjectTreeRootVM, ListMiddleParam, string> initMiddleMdToVM = null;
		public Func<DeviceType, ObjectTreeRootVM> getTreeRootVM = null;
		public Action<ObjectTreeRootVM> setTreeRootVM = null;

		public const int numMachineAddress = -1000;
		public const int lasserAddress = -2000;
		public const string logTitle = "[PLC监控线程]";
	}

	/// <summary>监控线程服务</summary>
	public class MonitorServer : IEquipments {
		public int maxShowItemCount = 4;    //最大可显示的项数量
		public int minTimeGap = 200;        //最小更新频率
		public int timeGap = 200;          //更新频率
		public ObjectItemVM selectTreeVM = null;//选中项
		public ObservableCollection<DevDetailItemVM> lastDetailVM = null;   //选中项的详细信息

		public Dictionary<DeviceType, ObjectTreeRootVM> mapTreeRoot = new Dictionary<DeviceType, ObjectTreeRootVM>();
		public Dictionary<ObjectItemVM, ListMiddleParam> mapVMToMd = new Dictionary<ObjectItemVM, ListMiddleParam>();       //ViewModel to Model
		public Dictionary<ListMiddleParam, ObjectItemVM> mapMdToVM = new Dictionary<ListMiddleParam, ObjectItemVM>();       //Model to ViewModel
		Dictionary<DeviceType, ComInfoCountPanelVM> mapAlarmCount = new Dictionary<DeviceType, ComInfoCountPanelVM>();    //报警计数
		AlarmStatus lastTotalStatus = AlarmStatus.Offline;  //总状态
		AlarmStatus lastDevStatus = AlarmStatus.Offline;    //设备状态
		bool isPlcOffline = true;                           //PLC是否离线
		bool isLaserOffline = true;                         //激光是否离线

		MonitorModel monitorMd = new MonitorModel();
		List<IMonitorCtl> lstCtl = new List<IMonitorCtl>();

		Timer timer = null;     //定时器
		bool isWaitDeal = false;//是否等待处理数据
		//CycleCount cycTimerEx = new CycleCount(true, 10 * 1000);    //异常发送最小间隔
		string resetParamValue = "1";       //复位功能写入参数

		public MonitorServer() {
			monitorMd.initMiddleMdToVM = initMiddleMdToVM;
			monitorMd.setTreeRootVM = setTreeRootVM;
			monitorMd.getTreeRootVM = getTreeRootVM;

			//get config
			try {
				timeGap = Int32.Parse(ConfigurationManager.AppSettings.Get("monitor_timGap"));
				timeGap = Math.Max(timeGap, minTimeGap);
			} catch(Exception) { }

			lstCtl.Add(new PLCMonitorCtl(monitorMd));
			lstCtl.Add(new NumMachineMonitorCtl(monitorMd));
			//lstCtl.Add(new LaserMonitorCtl(monitorMd));

			//监控面板
			var panel = MainModel.ins.mainWinVM.panel;
			mapAlarmCount[DeviceType.NumMachine] = panel.NumMachine;
			mapAlarmCount[DeviceType.Laser] = panel.Laser;
			mapAlarmCount[DeviceType.Tray] = panel.Tray;
			mapAlarmCount[DeviceType.Robot] = panel.Robot;
			mapAlarmCount[DeviceType.Ultrasonic] = panel.Ultrasonic;
		}

		public void Start() {
			try {
				initCmdServer();

				for(int i = 0; i < lstCtl.Count; ++i) {
					lstCtl[i].init();
				}

			} catch(Exception ex) {
				UILogServer.ins.error(MonitorModel.logTitle, ex.ToString());
			}

			timer = new Timer(onTimer, null, 0, timeGap);
		}

		public AbstractMessage GetMessage() {
			return null;
		}

		public void SetMessage(AbstractMessage message) {

		}

		public void Stop() {
			clear();
		}

		private void initCmdServer() {
			AsyncCmdServer.ins.listen(AsyncCmdType.PLCOffline, () => {
				//Debug.WriteLine("plc离线");
				if(isPlcOffline == false) {
					UILogServer.ins.error("PLC离线");
				}
				isPlcOffline = true;
				updateTotalStatus();
			});
			AsyncCmdServer.ins.listen(AsyncCmdType.PLCOnline, () => {
				//Debug.WriteLine("plc上线");
				if(isPlcOffline == true) {
					UILogServer.ins.info("PLC上线");
				}
				isPlcOffline = false;
				updateTotalStatus();
			});
			AsyncCmdServer.ins.listen(AsyncCmdType.Laser1Offline, () => {
				//Debug.WriteLine("plc上线");
				if(isLaserOffline == false) {
					UILogServer.ins.error("激光离线");
				}
				isLaserOffline = true;
				updateTotalStatus();
			});
			AsyncCmdServer.ins.listen(AsyncCmdType.Laser1Online, () => {
				//Debug.WriteLine("plc上线");
				if(isLaserOffline == true) {
					UILogServer.ins.info("激光上线");
				}
				isLaserOffline = false;
				updateTotalStatus();
			});
		}

		private void onTimer(object data) {
			if(isWaitDeal) {
				return;
			}

			isWaitDeal = true;
			Task.Run((Action)getData);
		}

		/// <summary>获取数据</summary>
		private void getData() {
			try {
				for(int i = 0; i < lstCtl.Count; ++i) {
					try {
						lstCtl[i].getData();
					} catch(Exception) {

					}
				}

				updateAsync();
			} catch(Exception) { }
			//end
			isWaitDeal = false;
		}

		public void clear() {
			try {
				if(timer != null) {
					timer.Dispose();
					timer = null;
				}
				
				for(int i = 0; i < lstCtl.Count; ++i) {
					lstCtl[i].clear();
				}

			} catch(Exception) {

			}
		}

		private void setTreeRootVM(ObjectTreeRootVM vm) {
			mapTreeRoot[vm.DevType] = vm;
		}

		private ObjectTreeRootVM getTreeRootVM(DeviceType type) {
			if(!mapTreeRoot.ContainsKey(type)) {
				return null;
			}
			return mapTreeRoot[type];
		}

		private void initMiddleMdToVM(ObjectTreeRootVM rootVM, ListMiddleParam leafMd, string pkey) {
			ObjectItemVM leafVM = new ObjectItemVM() {
				Name = leafMd.nameDesc,
				ShortName = leafMd.shortNameType,
				CanReset = leafMd.canReset,
				UiType = leafMd.uiType,
				Pkey = pkey,
				ImgType = leafMd.imgType,
				OverInfo = leafMd.nameDesc
			};
			mapVMToMd[leafVM] = leafMd;
			mapMdToVM[leafMd] = leafVM;

			rootVM.Add(leafVM);

			//遍历状态
			int itemCount = Math.Min(leafMd.Count, maxShowItemCount);
			for(int k = 0; k < itemCount; ++k) {
				var itemMd = leafMd[k];
				DevDetailItemVM itemVM = new DevDetailItemVM() {
					Name = itemMd.statusDesc,
					ShortName = itemMd.shortStatusDesc,
					CanReset = itemMd.canReset,
					CanWrite = (itemMd.ioType == ParamIOType.ReadWrite || itemMd.ioType == ParamIOType.WriteOnly),
					Address = itemMd.addr,
					RealValue = itemMd.value,
					Data = itemMd.value,
					ShortData = itemMd.value,
					LeftWidth = itemMd.statusDescWidth,
					AlarmStatus = AlarmStatus.None
				};

				if(itemMd.mapCbx.ContainsKey(itemMd.value)) {
					var cbx = itemMd.mapCbx[itemMd.value];
					itemVM.Data = cbx.desc;
					itemVM.ShortData = cbx.value;
				} else {
					if(itemMd.mapCbx.Count > 0) {
						itemVM.Data = "-";
						itemVM.ShortData = "-";
					}
				}
				
				leafVM.LstItem.Add(itemVM);
			}
		}

		/// <summary>异步更新Model到ViewModel</summary>
		private void updateAsync() {
			MainModel.ins.mainWin.Dispatcher.Invoke(update);
		}

		/// <summary>更新总状态</summary>
		private void updateTotalStatus() {
			AlarmStatus tempTotalStatus = AlarmStatus.Normal;
			if(isPlcOffline || isLaserOffline) {
				tempTotalStatus = AlarmStatus.Offline;
			}
			if((int)lastDevStatus > (int)tempTotalStatus) {
				tempTotalStatus = lastDevStatus;
			}

			if(tempTotalStatus == lastTotalStatus) {
				return;
			}

			//send command
			lastTotalStatus = tempTotalStatus;
			if(lastTotalStatus != AlarmStatus.None) {
				//send command
				AsyncCmdServer.ins.send(AsyncCmdType.TotalStatusChanged, lastTotalStatus);
			}
		}

		private void getAlarmStatus(DevParamModel md, out AlarmStatus status, out string strValue, out string shortValue) {
			strValue = "";
			shortValue = "";
			status = AlarmStatus.None;

			if(md.mapCbx.ContainsKey(md.value)) {
				//枚举型数据
				strValue = md.mapCbx[md.value].desc;
				shortValue = md.mapCbx[md.value].shortDesc;
				status = md.mapCbx[md.value].warnLevel;
			} else {
				if(md.mapCbx.Count > 0) {
					//不在枚举类型范围内
					strValue = md.value;
					shortValue = md.value;
					status = AlarmStatus.Error;
				} else {
					//非枚举类型
					strValue = md.value;
					shortValue = md.value;
					status = AlarmStatus.None;
				}
			}

			if(strValue == "") {
				strValue = "-";
				shortValue = "-";
			}
			if(md.addr == MonitorModel.numMachineAddress) {
				//号牌机
			} else if(md.addr == MonitorModel.lasserAddress) {
				//激光
				if(isLaserOffline) {
					setAlarmStatusOffline(md, out status, out strValue, out shortValue);
				}
			} else {
				//PLC
				if(isPlcOffline) {
					setAlarmStatusOffline(md, out status, out strValue, out shortValue);
				}
			}
		}

		private void setAlarmStatusOffline(DevParamModel md, out AlarmStatus status, out string strValue, out string shortValue) {
			if(md.mapCbx.Count != 0 && md.mapCbx.First().Value.warnLevel != AlarmStatus.None) {
				status = AlarmStatus.Offline;
			} else {
				status = AlarmStatus.None;
			}
			//status = AlarmStatus.Offline;
			strValue = "-";
			shortValue = "-";
		}

		private void getAlarmStatus(DevParamModel md, DevDetailItemVM vm) {
			string value = "";
			string shortValue = "";
			AlarmStatus status = AlarmStatus.None;

			getAlarmStatus(md, out status, out value, out shortValue);

			if(value == "") {
				value = "-";
				shortValue = "-";
			}
			vm.RealValue = md.value;
			vm.Data = value;
			vm.ShortData = shortValue;
			vm.AlarmStatus = status;
		}

		/// <summary>更新Model到ViewModel</summary>
		private void update() {
			foreach(DeviceType dev in mapAlarmCount.Keys) {
				mapAlarmCount[dev].normal.Num = 0;
				mapAlarmCount[dev].warn.Num = 0;
				mapAlarmCount[dev].error.Num = 0;
			}

			//遍历小类
			lastDevStatus = AlarmStatus.Normal;
			foreach(ListMiddleParam md in mapMdToVM.Keys) {
				ObjectItemVM vm = mapMdToVM[md];
				ComInfoCountPanelVM countCtl = null;
				if(mapAlarmCount.ContainsKey(md.type)) {
					countCtl = mapAlarmCount[md.type];
				}

				int itemCount = Math.Min(md.Count, maxShowItemCount);
				if(vm.LstItem.Count > md.Count) {
					continue;
				}
				AlarmStatus alarmStatus = AlarmStatus.None;
				//遍历状态-前几个
				for(int i = 0; i < itemCount; ++i) {
					DevParamModel itemMd = md[i];
					DevDetailItemVM itemVM = vm.LstItem[i];
					itemVM.RealValue = itemMd.value;

					getAlarmStatus(itemMd, itemVM);
					//计数
					addPanelCount(countCtl, itemVM.AlarmStatus);
					//
					//itemVM.AlarmStatus = (AlarmStatus)rad.Next(Enum.GetNames(typeof(AlarmStatus)).Length);
					if((int)itemVM.AlarmStatus > (int)alarmStatus) {
						alarmStatus = itemVM.AlarmStatus;
					}
				}
				//遍历状态-剩余部分
				for(int i = itemCount; i < md.Count; ++i) {
					DevParamModel itemMd = md[i];

					string value = "";
					string shortValue = "";
					AlarmStatus status = AlarmStatus.None;
					getAlarmStatus(itemMd, out status, out value, out shortValue);
					addPanelCount(countCtl, status);
					if((int)status > (int)alarmStatus) {
						alarmStatus = status;
					}
				}
				vm.ShortInfo = md.value;
				//小类总状态
				vm.AlarmStatus = alarmStatus;
				if((int)alarmStatus > (int)lastDevStatus) {
					lastDevStatus = alarmStatus;
				}
			}
			updateTotalStatus();
			//选中的详细信息
			if(selectTreeVM != null) {
				setDetailViewModel(selectTreeVM, lastDetailVM);
			}
		}

		/// <summary>报警计数</summary>
		private void addPanelCount(ComInfoCountPanelVM ctl, AlarmStatus status) {
			if(ctl == null) {
				return;
			}
			switch(status) {
			case AlarmStatus.Normal: ++ctl.normal.Num; break;
			case AlarmStatus.Warning:
			case AlarmStatus.Reset:
				++ctl.warn.Num; break;
			case AlarmStatus.Error:
			case AlarmStatus.Offline:
				++ctl.error.Num; break;
			}
		}

		/// <summary>获取选择的详细信息ViewModel</summary>
		public ObservableCollection<DevDetailItemVM> getSelectedDetailViewModel(ObjectItemVM treeVM) {
			selectTreeVM = null;
			lastDetailVM = null;

			var lstDetailVM = getDetailViewModel(treeVM);
			if(lstDetailVM == null) {
				return lstDetailVM;
			}

			selectTreeVM = treeVM;
			lastDetailVM = lstDetailVM;

			return lstDetailVM;
		}

		/// <summary>获取详细信息ViewModel</summary>
		public ObservableCollection<DevDetailItemVM> getDetailViewModel(ObjectItemVM treeVM) {
			if(!mapVMToMd.ContainsKey(treeVM)) {
				return null;
			}

			var lstDetailVM = new ObservableCollection<DevDetailItemVM>();
			//selectTreeVM = treeVM;
			//lastDetailVM = lstDetailVM;
			setDetailViewModel(treeVM, lstDetailVM);

			return lstDetailVM;
		}

		/// <summary>设置详细信息ViewModel</summary>
		private void setDetailViewModel(ObjectItemVM treeVM, ObservableCollection<DevDetailItemVM> lstDetailVM) {
			ListMiddleParam md = mapVMToMd[treeVM];

			//lstDetailVM.Clear();
			bool isNewVM = lstDetailVM.Count == 0;

			//设备名
			if(isNewVM) {
				DevDetailItemVM itemName = new DevDetailItemVM() {
					Name = Language.ins.devName,
					Data = md.nameDesc,
					AlarmStatus = AlarmStatus.None
				};
				lstDetailVM.Add(itemName);
			}

			//设备状态
			for(int i = 0; i < md.Count; ++i) {
				DevDetailItemVM itemVM = null;
				if(isNewVM) {
					itemVM = new DevDetailItemVM() {
						Name = md[i].statusDesc,
						//Data = strValue,
						Address = md[i].addr,
						//RealValue = md[i].value,
						CanWrite = (md[i].ioType == ParamIOType.ReadWrite || md[i].ioType == ParamIOType.WriteOnly),
						CanReset = md[i].canReset,
						//AlarmStatus = status
					};
					lstDetailVM.Add(itemVM);
				} else {
					if(i + 1 < lstDetailVM.Count) {
						itemVM = lstDetailVM[i + 1];
					}
				}
				getAlarmStatus(md[i], itemVM);
			}
		}

		private string getCol(Dictionary<string, int> mapHead, List<string> arrCol, string strCol) {
			if(!mapHead.ContainsKey(strCol)) {
				return "";
			}

			int idx = mapHead[strCol];
			if(idx < 0 || idx >= arrCol.Count) {
				return "";
			}

			return arrCol[idx];
		}

		public DevParamModel getParamModel(ObjectItemVM treeVM, int idx) {
			ListMiddleParam md = mapVMToMd[treeVM];
			return md[idx - 1];
		}

		/// <summary>写入到PLC</summary>
		public void writeData(ObjectItemVM treeVM, int idx, string value) {
			var md = getParamModel(treeVM, idx);
			writeData(md.addr, md.valueType, value);
		}

		/// <summary>写入到PLC</summary>
		public void writeData(DevParamModel md, string value) {
			writeData(md.addr, md.valueType, value);
		}

		/// <summary>写入到PLC</summary>
		public void writeData(int address, ParamValueType type, string value) {
			Task.Run(() => {
				try {
					//plcCtl.write((ushort)address, type, value);
					IEquipments plcTh = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.PLC);
					if(plcTh == null) {
						return;
					}
					plcTh.SetMessage(new PLCNode() {
						Address = address.ToString(),
						Value = value
					});
					//UILogServer.ins.log(logTitle, "写入成功");
				} catch(Exception ex) {
					UILogServer.ins.error(MonitorModel.logTitle, ex.ToString());
				}
			});
		}

		/// <summary>复位</summary>
		public void restData(ObjectItemVM treeVM, int idx) {
			//writeData(treeVM, idx, "1");
			var md = getParamModel(treeVM, idx);
			resetData(md.addr, md.valueType);
		}

		/// <summary>复位</summary>
		private void resetData(int address, ParamValueType type) {
			Task.Run(() => {
				try {
					//plcCtl.write((ushort)address, type, resetParamValue);
					IEquipments plcTh = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.PLC);
					if(plcTh == null) {
						return;
					}
					plcTh.SetMessage(new PLCNode() {
						Address = address.ToString(),
						Value = resetParamValue
					});
					//UILogServer.ins.log(logTitle, "复位成功");
				} catch(Exception ex) {
					UILogServer.ins.error(MonitorModel.logTitle + " 复位", ex.ToString());
				}
			});
		}

	}

}
